﻿using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;

namespace JoinBoxAlignPlug
{
    public static class CodeHelper
    {
        public const string _annotationLeft = "/*";
        public const string _annotationRight = "*/";
        public const string _enum = "enum";
        public const string _curlyBracesLeft = "{";
        public const string _curlyBracesRight = "}";
        public const string _annotation = "//";
        public static string[] ReservedWord = new[] { "return", "continue", "break" };

        /// <summary>
        /// 0位置到第一个符号的长度
        /// </summary>
        /// <param name="hang">当前行</param>
        /// <param name="length">第一个符号的长度位置</param>
        /// <returns></returns>
        public static void GetText2FuhaoLenth(Encoding encoding, string hang, int length, ref int max)
        {
            //可以通过获取当前文件的编码,通过它获取字符串长度,例如中文对齐需要这个...
            //唉,那默认Encoding.Default岂不是非通用?
#if true
            //处理中文对齐的
            var qian = hang.Substring(0, length);
            var bys = encoding.GetByteCount(qian);
#else
            //没有处理中文的
            var bys  = length;
#endif
            max = bys > max ? bys : max;//记录符号最长位置,用于对齐
        }

        /// <summary>
        /// 获取非字符串的符号位置,[0]第一个出现.
        /// 但是hang是纯/**/包裹内,也返回符号位置
        /// </summary>
        /// <param name="hang">c#的字符串需要在一行完成</param>
        /// <param name="fuhao">符号</param>
        /// <param name="all">返回的符号</param>
        /// <returns></returns>
        public static void GetFuhaoIndex(string hang, string fuhao, List<int> all)
        {
            if (hang.Length == 0)
                return;
            int number = 0;
            string qianmian;
            while (number < hang.Length)
            {
                number = hang.IndexOf(fuhao, number);
                if (number == -1)
                    break;
                qianmian = hang.Substring(0, number);
                qianmian = qianmian.Replace("\\\"", string.Empty);//剔除字符串内的引号
                var fuHaos1 = GetIndexOfAll(qianmian, "\"");      //所有的引号位置,不需要累加,因为每次从0开始拿

                if (fuHaos1.Count % 2 != 0)
                {
                    //判断最后一个引号在/**/内部:
                    int yinhaoLast = fuHaos1[fuHaos1.Count - 1];
                    var qian = hang.LastIndexOf(_annotationLeft, yinhaoLast);//从后往前
                    if (qian != -1)
                    {
                        var hou = hang.IndexOf(_annotationRight, yinhaoLast);//从前往后
                        if (hou != -1)
                            fuHaos1.RemoveAt(fuHaos1.Count - 1);
                    }
                }
                //模2的余数只会是0或1,0代表前面的引号全部配对的
                if (fuHaos1.Count % 2 == 0)
                    all.Add(number);//加入符号所在位置
                number = ++number;
            }
        }

        /// <summary>
        /// 获取符号所有位置
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static List<int> GetIndexOfAll(string str, string fuHao)
        {
            var ints = new List<int>();
            int idx = 0;
            while ((idx = str.IndexOf(fuHao, idx)) != -1)
            {
                ints.Add(idx);
                ++idx;//向下一位搜索
            }
            return ints;
        }

        #region 识别注释范围

        /// <summary>
        /// 获取当前行/*和*/的所有位置,并且移除前后差一的项
        /// </summary>
        /// <param name="hang"></param>
        /// <param name="zsf_kai"></param>
        /// <param name="zsf_bi"></param>
        static void GetSplitCommentSymbolLocation(string hang, List<int> zsf_kai, List<int> zsf_bi)
        {
            hang = hang.Trim();
            GetFuhaoIndex(hang, _annotationLeft, zsf_kai);
            if (zsf_kai.Count != 0)
            {
                foreach (var kai in zsf_kai)
                {
                    var kai2 = kai + _annotationLeft.Length;
                    var bi = hang.IndexOf(_annotationRight, kai2);//注释符*/即使是字符串内也可识别的
                    if (bi != -1)                                 //找到闭号
                        zsf_bi.Add(bi);
                }
            }

            //  zsf_kai  List<int>(3) { 0, 18, 22 }//要剔除0~6之间的第二位
            //  zsf_bi   List<int>(2) { 19, 23 }   //23比22小一,而/*或者*/是2位的,所以要剔除这个23
            for (int i = 0; i < zsf_bi.Count; i++)
            {
                for (int j = i + 1; j < zsf_kai.Count; j++)
                {
                    if (zsf_kai[j] < zsf_bi[i])
                    {
                        zsf_kai.RemoveAt(j);
                        j--;
                    }
                }
            }

            var ints = new List<int>();
            for (int i = 0; i < zsf_bi.Count; i++)
            {
                int j = i + 1;
                if (j < zsf_kai.Count && zsf_bi[i] - zsf_kai[j] == -1)
                    ints.Add(j);
            }
            ints.Reverse();
            foreach (var item in ints)//从最高位开始移除
                zsf_kai.RemoveAt(item);

            //剔除末尾向近
            if (zsf_bi.Count > 0 && zsf_kai.Count > 0 &&
                zsf_bi[zsf_bi.Count - 1] - zsf_kai[zsf_kai.Count - 1] == 1)
                zsf_bi.RemoveAt(zsf_bi.Count - 1);
        }

        /// <summary>
        /// 正则匹配两个指定字符串之间的字符串
        /// </summary>
        /// <param name="sourse">字符串</param>
        /// <param name="startstr">开头符号</param>
        /// <param name="endstr">尾巴符号</param>
        /// <returns>组</returns>
        ///  var sts = MatchGetMids("/*////*///*/",  "\\*/","/\\*"); 这里不行,没有考虑到/* /*/
        ///  var sts = MatchGetMids("asd/*ddd*/ccc/*eee*/", "/\\*", "\\*/");       //要对*转义
        static MatchCollection MatchGetMids(string sourse, string startstr, string endstr)
        {
            var re = "(?<=(" + startstr + "))[.\\s\\S]*?(?=(" + endstr + "))";
            var r = new Regex(re, RegexOptions.Multiline | RegexOptions.Singleline);
            return r.Matches(sourse);
        }

        // GetCommentTexts("///*aa*/wwwwwwww/*a\"a\"a*/nnnnnnnn/*b\"b\"b*/")
        // GetCommentTexts("/*aa*/wwwwwwww/*a\"a\"a*/nnnnnnnn/*b\"b\"b*/")
        // GetCommentTexts("aaaaa`if /*//vv*///cc")
        // GetCommentTexts("aaaaa//vvvv")
        /// <summary>
        /// 获取段落注释,本行含有(非字符串的/*) 还有 (*/的用无论字符串非字符串其一即可断言)
        /// </summary>
        /// <param name="hang">行</param>
        /// <returns>返回注释部分</returns>
        public static List<string> GetCommentTexts(string hang)
        {
            hang = hang.Trim(' ', '\t');
            var ret = new List<string>();//返回的

            //  var hang = "/*/////////////////*///*/";
            //  var hang = "/*/////////////////*/*//*/";   这个测试案例特别
            //  var hang = "aaaaa`if /*//vv*///cc";
            //  var hang ="aaaaa//vvvv";
            //  var hang = "///*aa*/wwwwwwww/*a\"a\"a*/nnnnnnnn/*b\"b\"b*/";
            var zsf_kai = new List<int>();//开号位置
            var zsf_bi = new List<int>(); //闭号位置

            GetSplitCommentSymbolLocation(hang, zsf_kai, zsf_bi);

            //获取所有//位置
            var oneIndexOf = new List<int>();
            GetFuhaoIndex(hang, _annotation, oneIndexOf);

            //剔除 kai[]~bi[]之间的
            for (int ss = 0; ss < oneIndexOf.Count; ss++)
                for (int i = 0; i < zsf_kai.Count; i++)
                {
                    if (i < zsf_bi.Count)
                    {
                        if (zsf_kai[i] < oneIndexOf[ss] && oneIndexOf[ss] < zsf_bi[i])
                        {
                            oneIndexOf.RemoveAt(ss);
                            ss--;
                        }
                    }
                }

            //闭号后面*//要剔除
            for (int ss = 0; ss < oneIndexOf.Count; ss++)
                for (int i = 0; i < zsf_bi.Count; i++)
                {
                    if (oneIndexOf[ss] == zsf_bi[i] + 1)
                    {
                        oneIndexOf.RemoveAt(ss);
                        ss--;
                    }
                }

            string houmian = null;
            if (oneIndexOf.Count > 0)
            {
                int oofOK = oneIndexOf[0];

                //获取后面的注释内容
                houmian = hang.Substring(oofOK, hang.Length - oofOK);//后面的注释内容

                //扔掉注释的后面
                hang = hang.Substring(0, oofOK); //前面的代码和注释内容
            }

            //获取/**/之间的内容
            var sts = MatchGetMids(hang, "/\\*", "\\*/");
            foreach (Match item in sts)
                ret.Add(item.Value);

            if (houmian != null)
                ret.Add(houmian);

            return ret;
        }
        #endregion

        /// <summary>
        /// 获取段落注释,本行含有(非字符串的/*) 还有 (*/的用无论字符串非字符串其一即可断言)
        /// </summary>
        /// <param name="hang">行</param>
        /// <returns>返回代码部分</returns>
        public static List<string> GetCommentCodes(string hang)
        {
            var lst = new List<string>();
            var zsf_kai = new List<int>();
            GetFuhaoIndex(hang, _annotationLeft, zsf_kai);
            if (zsf_kai.Count != 0)//如果找到了
            {
                int zsf_bi = 0;
                foreach (var kai in zsf_kai)
                {
                    var sdf = hang.Substring(zsf_bi, kai - zsf_bi);
                    if (!string.IsNullOrEmpty(sdf.Trim()))
                        lst.Add(sdf);
                    zsf_bi = hang.IndexOf(_annotationRight, kai);//注释符*/即使是字符串内也可识别的
                    if (zsf_bi == -1)                            //找不到闭号
                        break;
                    zsf_bi += _annotationRight.Length;
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(hang.Trim()))
                    lst.Add(hang);
            }
            return lst;
        }

        /// <summary>
        /// 去除注释,返回某行非注释部分(预处理也扔掉)
        /// </summary>
        /// <returns></returns>
        public static string GetCode(string hang)
        {
            var oneIndexOf = new List<int>();
            GetFuhaoIndex(hang, _annotation, oneIndexOf);
            if (oneIndexOf.Count != 0)
            {
                //预处理也可以注释,所以需要经过下面
                hang = hang.Substring(0, oneIndexOf[0]);
            }
            hang = hang.Trim(' ', '\t');
            if (string.IsNullOrEmpty(hang) || hang[0] == '#')//预处理行抛弃,CS1040说明位置
                return string.Empty;
            return hang;
        }

    }
}
